package com.zrfintech.api.controller;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.zrfintech.api.vo.ApiRequestVo;
import com.zrfintech.api.vo.ApiResponseVo;
import com.zrfintech.common.RequestContext;
import com.zrfintech.common.concurrent.SparrowThreadPoolTaskExecutor;
import com.zrfintech.common.enums.RestRetCodeEnum;
import com.zrfintech.common.util.SignUtil;
import com.zrfintech.constant.RequestContextField;
import com.zrfintech.core.service.cache.InstitutionCache;
import com.zrfintech.core.service.cache.ProductCache;
import com.zrfintech.core.service.cache.ProductInstitutionCache;
import com.zrfintech.core.service.product.ProductService;
import com.zrfintech.dao.entity.MgInstitutionEntity;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping(value = "/api/v1")
public class ApiController extends BaseController {

    private static Logger LOGGER = LoggerFactory.getLogger(ApiController.class);

    @Resource(name = "apiTimeOutControlPool")
    private SparrowThreadPoolTaskExecutor apiTimeOutThreadPool;
    @Resource
    private Map<String, ProductService> productServiceMap;

    @PostMapping("/product")
    public Object product(@RequestBody ApiRequestVo requestVo) {
        if (requestVo == null) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参为空");
        }
        if (StringUtils.isEmpty(requestVo.getTimeStr())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参time为空");
        }
        if (StringUtils.isEmpty(requestVo.getProductId())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参product为空");
        }
        if (StringUtils.isEmpty(requestVo.getInstId())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参instId为空");
        }
        if (StringUtils.isEmpty(requestVo.getRequestId())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参requestId为空");
        }
        if (StringUtils.isEmpty(requestVo.getSign())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参sign为空");
        }
        if (MapUtils.isEmpty(requestVo.getData())) {
            return new ApiResponseVo(RestRetCodeEnum.PARAMETER_ERROR, "入参data为空");
        }
        ApiResponseVo response = null;
        RequestContext.setFirstContext(RequestContextField.REQCTX_FIELD_PRODUCTID, requestVo.getProductId());
        RequestContext.setFirstContext(RequestContextField.REQCTX_FIELD_INSTID, requestVo.getInstId());
        RequestContext.setFirstContext(RequestContextField.REQCTX_FIELD_REQUESTID, requestVo.getRequestId());
        RequestContext.setSecondContext(RequestContextField.REQCTX_COMON_INSTREQPMS, JSONObject.toJSONString(requestVo));
        response = checkAndValid(requestVo);
        LOGGER.info("[productOutput]request-ip={}, productId={}, requestParam={}.", getRemoteIP(),
                requestVo.getProductId(), requestVo.getData().toString());
        try {
            if (response == null) {
                response = new ApiResponseVo();
                ProductService productService = productServiceMap.get(requestVo.getProductId());
                Object data = apiTimeOutControl(productService, new JSONObject());
                response.setData(data);
            } else {
                LOGGER.warn("[productOutput]ValidateParams failed response={}.", response.toString());
            }
        } catch (Exception e) {
            LOGGER.error("[productOutput][{}].", e.toString(), e);
            response = new ApiResponseVo();
            response.setStatus(RestRetCodeEnum.INTERNAL_ERROR.getValue());
            response.setMsg(RestRetCodeEnum.INTERNAL_ERROR.getMsg());
        } finally {
            requestFinally(response, requestVo.getInstId(), requestVo.getProductId());
        }

        return response;
    }

    private ApiResponseVo checkAndValid(ApiRequestVo apiRequestVo) {
        ApiResponseVo response = new ApiResponseVo();
        response.setRequestId(apiRequestVo.getRequestId());
        String productId = apiRequestVo.getProductId();
        if (!ProductCache.ProductChcheMap.containsKey(productId)) {
            // 产品不存在
            response.setStatus(RestRetCodeEnum.PARAMETER_ERROR.getValue());
            response.setMsg(RestRetCodeEnum.PARAMETER_ERROR.getMsg("产品不存在 productId=" + productId));
            return response;
        }
        String instId = apiRequestVo.getInstId();
        if (!InstitutionCache.institutionChcheMap.containsKey(instId)) {
            // 机构不存在
            response.setStatus(RestRetCodeEnum.PARAMETER_ERROR.getValue());
            response.setMsg(RestRetCodeEnum.PARAMETER_ERROR.getMsg("机构不存在 instId=" + instId));
            return response;
        }
        // 机构无权限
        List<String> productIdList = ProductInstitutionCache.institutionProductChcheMap.get(instId);
        if (CollectionUtils.isEmpty(productIdList) || !productIdList.contains(productId)) {
            response.setStatus(RestRetCodeEnum.NO_AUTH.getValue());
            response.setMsg(RestRetCodeEnum.NO_AUTH.getMsg());
            return response;
        }
        // 签名验证
        MgInstitutionEntity institution = InstitutionCache.institutionChcheMap.get(instId);
        String institutionSk = institution.getInstitutionSk();
        String sign = apiRequestVo.getSign();
        String requestId = apiRequestVo.getRequestId();
        String time = apiRequestVo.getTimeStr();
        String localSign = null;
        if (StringUtils.length(sign) == 32) {
            localSign = SignUtil.generateSignValue(institutionSk, requestId, time);
        } else if (StringUtils.length(sign) == 64) { // sign = SHA256(sk&requestId&time)
            localSign = SignUtil.generateSHA256Sign(institutionSk, requestId, time);
        } else {
            response.setStatus(RestRetCodeEnum.INVALID_SIGN.getValue());
            response.setMsg(RestRetCodeEnum.INVALID_SIGN.getMsg());
            return response;
        }
        if (!localSign.equals(sign)) {
            response.setStatus(RestRetCodeEnum.INVALID_SIGN.getValue());
            response.setMsg(RestRetCodeEnum.INVALID_SIGN.getMsg());
            return response;
        }
        //产品特定的参数校验
        return null;
    }

    // 无论是超时还是正常返回均返回正常
    private Object apiTimeOutControl(final ProductService productService, final JSONObject requestParam) {
        final Map<String, Object> reqContextMap = RequestContext.get();
//        final String logId = reqContextMap.get(FormatService.LOG_ID).toString();
        Future<Object> future = apiTimeOutThreadPool.submit(
                new Callable<Object>() {
                    @Override
                    public Object call() throws Exception {
//                        MDCUtils.setLogId(logId);
                        RequestContext.set(reqContextMap);
                        return productService.productOutput(requestParam);
                    }
                }
        );

        Object finalResult = null;
        try {
            finalResult = future.get(2000L, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            future.cancel(true);
            finalResult = productService.defaultResult(requestParam);
            LOGGER.error("[apiTimeOutControl][{}].", e.toString(), e);
        }
        return finalResult;
    }


    /**
     * 保存请求响应结果
     */
    private void requestFinally(ApiResponseVo response, String institutionId, String productId) {
        try {
            RequestContext.setFirstContext(RequestContextField.REQCTX_FIELD_RESPONJSON, JSONObject.toJSONString(response, SerializerFeature.WriteMapNullValue));
            RequestContext.setFirstContext(RequestContextField.REQCTX_FIELD_RESPONSTATUS, response.getStatus());


        } catch (Exception e) {
            LOGGER.error("[requestFinally]exception.", e);
        }
    }
}


